using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace NBear.Common
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
    public class PrimaryKeyAttribute : Attribute
    {
        public PrimaryKeyAttribute()
        {
        }
    }

    [AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
    public class ColumnNameAttribute : Attribute
    {
        private string name;

        public string Name
        {
            get
            {
                return this.name;
            }
        }

        public ColumnNameAttribute(string name) 
        {
            this.name = name;
        }
    }

    [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)]
    public class TableAttribute : Attribute
    {
        private string name;
        private bool isView = false;
        private string additionalWhere;
        private string additionalInsert;
        private bool isCascadeUpdate;
        private bool isContract;
        private string updateTableName;

        public string Name
        {
            get
            {
                return this.name;
            }
        }

        public bool IsView
        {
            get
            {
                return this.isContract || this.isView;
            }
            set
            {
                if (name != null && name.ToLower().Contains(" from "))
                {
                    name = string.Format("#_Begin# ({0}) __AliasName #_End#", name);
                }

                this.isView = value;
            }
        }

        public bool IsCascadeUpdate
        {
            get
            {
                return isCascadeUpdate;
            }
            set
            {
                isCascadeUpdate = value;
            }
        }

        public bool IsContract
        {
            get
            {
                return isContract;
            }
            set
            {
                isContract = value;
            }
        }

        public string AdditionalWhere
        {
            get
            {
                return additionalWhere;
            }
            set
            {
                additionalWhere = value;
            }
        }

        public string AdditionalInsert
        {
            get
            {
                return additionalInsert;
            }
            set
            {
                additionalInsert = value;
            }
        }

        public string UpdateTableName
        {
            get
            {
                return updateTableName;
            }
            set
            {
                updateTableName = value;
            }
        }

        public TableAttribute()
        {
        }

        public TableAttribute(string name) : this(name, false)
        {
        }

        public TableAttribute(string name, bool isView)
        {
            this.name = name;
            this.isView = isView;
        }

        public TableAttribute(bool isView)
        {
            this.isView = isView;
        }
    }

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public class CustomPropertyAttribute : Attribute
    {
        private string[] parameters;

        public string[] Parameters
        {
            get
            {
                return parameters;
            }
        }

        public CustomPropertyAttribute(params string[] parameters)
        {
            this.parameters = parameters;
        }
    }

    public class EntityConfiguration
    {
        public string EntityFullName;
        public TableAttribute Table;
        internal ColumnConfiguration[] Columns;
        public StoreProcedureAttribute StoredProcedure;

        public PrimaryKeyAttribute GetPrimaryKey(string name)
        {
            if (Columns == null)
            {
                return null;
            }

            foreach (ColumnConfiguration column in Columns)
            {
                if (column.PropertyName == name && column.IsPrimaryKey)
                {
                    return new PrimaryKeyAttribute();
                }
            }

            return null;
        }

        public ColumnNameAttribute GetColumnName(string name)
        {
            if (Columns == null)
            {
                return null;
            }

            foreach (ColumnConfiguration column in Columns)
            {
                if (column.PropertyName == name && column.ColumnName != null)
                {
                    return new ColumnNameAttribute(column.ColumnName);
                }
            }

            return null;
        }

        public CustomPropertyAttribute GetCustomProperty(string name)
        {
            if (Columns == null)
            {
                return null;
            }

            foreach (ColumnConfiguration column in Columns)
            {
                if (column.PropertyName == name && column.IsCustomProperty)
                {
                    return new CustomPropertyAttribute(column.Parameters);
                }
            }

            return null;
        }
    }

    public class ColumnConfiguration
    {
        public string PropertyName;
        public string ColumnName;
        public bool IsPrimaryKey;
        public bool IsCustomProperty;
        public string[] Parameters;
    }

    public class EntityConfigurationCollection
    {
        internal EntityConfiguration[] EntityConfiurations;

        public EntityConfiguration this[string entityFullName]
        {
            get
            {
                if (EntityConfiurations == null)
                {
                    return null;
                }

                foreach (EntityConfiguration ec in EntityConfiurations)
                {
                    if (ec.EntityFullName == entityFullName)
                    {
                        return ec;
                    }
                }

                return null;
            }
        }
    }

    public class EntityConfigurationSection : System.Configuration.IConfigurationSectionHandler
    {
        private static EntityConfigurationCollection entityConfigs;

        #region IConfigurationSectionHandler Members

        public object Create(object parent, object configContext, XmlNode section)
        {
            if (entityConfigs != null)
            {
                return entityConfigs;
            }

            entityConfigs = new EntityConfigurationCollection();

            XmlNodeList entityNodes = section.SelectNodes("Entity");
            if (entityNodes != null && entityNodes.Count > 0)
            {
                entityConfigs.EntityConfiurations = new EntityConfiguration[entityNodes.Count];
                for (int i = 0; i < entityNodes.Count; i++)
                {
                    entityConfigs.EntityConfiurations[i] = new EntityConfiguration();

                    entityConfigs.EntityConfiurations[i].EntityFullName = GetNodeValue(entityNodes[i].Attributes["fullName"]);
                    if (!string.IsNullOrEmpty(GetNodeValue(entityNodes[i].Attributes["storedProcedure"])))
                    {
                        entityConfigs.EntityConfiurations[i].StoredProcedure = new StoreProcedureAttribute();
                        entityConfigs.EntityConfiurations[i].StoredProcedure.Name = GetNodeValue(entityNodes[i].Attributes["storedProcedure"]);
                        entityConfigs.EntityConfiurations[i].StoredProcedure.FillByIndex = GetBoolNodeValue(entityNodes[i].Attributes["storedProcedureFillByIndex"]);
                    }
                    

                    XmlNode tableNode = entityNodes[i].SelectSingleNode("Table");
                    if (tableNode != null)
                    {
                        entityConfigs.EntityConfiurations[i].Table = new TableAttribute(GetNodeValue(tableNode.SelectSingleNode("Name")), GetBoolNodeValue(tableNode.Attributes["isView"]));
                        entityConfigs.EntityConfiurations[i].Table.AdditionalInsert = GetNodeValue(tableNode.SelectSingleNode("AdditionalInsert"));
                        entityConfigs.EntityConfiurations[i].Table.AdditionalWhere = GetNodeValue(tableNode.SelectSingleNode("AdditionalWhere"));
                        entityConfigs.EntityConfiurations[i].Table.IsCascadeUpdate = GetBoolNodeValue(tableNode.Attributes["isCascadeUpdate"]);
                        entityConfigs.EntityConfiurations[i].Table.IsContract = GetBoolNodeValue(tableNode.Attributes["isContract"]);
                        entityConfigs.EntityConfiurations[i].Table.UpdateTableName = GetNodeValue(tableNode.Attributes["updateTableName"]);
                    }

                    XmlNode columnsNode = entityNodes[i].SelectSingleNode("Columns");
                    if (columnsNode != null)
                    {
                        XmlNodeList columnNodes = columnsNode.SelectNodes("Column");

                        if (columnNodes != null && columnNodes.Count > 0)
                        {
                            entityConfigs.EntityConfiurations[i].Columns = new ColumnConfiguration[columnNodes.Count];

                            int j = 0;
                            foreach (XmlNode columnNode in columnNodes)
                            {
                                ColumnConfiguration cc = new ColumnConfiguration();
                                cc.PropertyName = GetNodeValue(columnNode.Attributes["propertyName"]);
                                cc.ColumnName = GetNodeValue(columnNode.Attributes["columnName"]);
                                cc.IsPrimaryKey = GetBoolNodeValue(columnNode.Attributes["isPrimaryKey"]);
                                cc.IsCustomProperty = GetBoolNodeValue(columnNode.Attributes["isCustomProperty"]);

                                XmlNode parametersNode = columnNode.SelectSingleNode("Parameters");

                                if (parametersNode != null)
                                {
                                    XmlNodeList parameterNodes = parametersNode.SelectNodes("Parameter");

                                    if (parameterNodes != null && parameterNodes.Count > 0)
                                    {
                                        cc.Parameters = new string[parameterNodes.Count];

                                        int k = 0;
                                        foreach (XmlNode parameterNode in parameterNodes)
                                        {
                                            cc.Parameters[k] = GetNodeValue(parameterNode);

                                            k++;
                                        }

                                    }
                                }

                                entityConfigs.EntityConfiurations[i].Columns[j] = cc;
                                j++;
                            }
                        }
                    }
                }
            }

            return entityNodes;
        }

        private string GetNodeValue(XmlNode node)
        {
            if (node != null)
            {
                return node.InnerXml;
            }

            return null;
        }

        private bool GetBoolNodeValue(XmlNode node)
        {
            string str = GetNodeValue(node);

            if (str != null && str.ToLower() == "true")
            {
                return true;
            }

            return false;
        }

        #endregion
    }

    [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)]
    public class StoreProcedureAttribute : Attribute
    {
        private string name;
        private bool fillByIndex = false;

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public bool FillByIndex
        {
            get
            {
                return fillByIndex;
            }
            set
            {
                fillByIndex = value;
            }
        }

        public StoreProcedureAttribute()
        {
        }

        public StoreProcedureAttribute(string name)
        {
            this.name = name;
        }
    }
}
